import React, { useRef } from 'react'
import { useDrag, useDrop } from 'react-dnd'
import type { Identifier, XYCoord } from 'dnd-core'
import { SceneCard, type SceneCardProps } from './index'
import { DragTypes, type DragItem } from '../types'

export interface DraggableSceneCardProps extends SceneCardProps {
  onMove: (fromIndex: number, toIndex: number) => void
}

/**
 * 可拖拽场景卡片组件
 * 基于SceneCard添加拖拽排序功能
 */
export const DraggableSceneCard: React.FC<DraggableSceneCardProps> = ({
  scene,
  index,
  onMove,
  ...cardProps
}) => {
  const ref = useRef<HTMLDivElement>(null)

  // 拖拽配置
  const [{ isDragging }, drag] = useDrag({
    type: DragTypes.SCENE,
    item: (): DragItem => ({
      id: scene.id,
      index,
      type: DragTypes.SCENE
    }),
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  })

  // 放置配置
  const [{ handlerId }, drop] = useDrop<
    DragItem,
    void,
    { handlerId: Identifier | null }
  >({
    accept: DragTypes.SCENE,
    collect(monitor) {
      return {
        handlerId: monitor.getHandlerId(),
      }
    },
    hover(item: DragItem, monitor) {
      if (!ref.current) {
        return
      }
      const dragIndex = item.index
      const hoverIndex = index

      // 如果拖拽目标是自己，不处理
      if (dragIndex === hoverIndex) {
        return
      }

      // 获取拖拽元素的边界
      const hoverBoundingRect = ref.current?.getBoundingClientRect()

      // 获取垂直中心点
      const hoverMiddleY =
        (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2

      // 获取鼠标位置
      const clientOffset = monitor.getClientOffset()

      // 获取相对于hover目标顶部的垂直位置
      const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top

      // 只有当鼠标跨越了一半区域时才移动
      // 向下拖拽时，只有当鼠标在下半部分时才移动
      if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
        return
      }

      // 向上拖拽时，只有当鼠标在上半部分时才移动
      if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
        return
      }

      // 执行移动
      onMove(dragIndex, hoverIndex)

      // 更新拖拽项的索引
      // 注意: 这里修改item对象，因为这样可以避免昂贵的索引搜索
      item.index = hoverIndex
    },
  })

  // 合并refs
  drag(drop(ref))

  const opacity = isDragging ? 0.4 : 1

  return (
    <div 
      ref={ref} 
      style={{ opacity }}
      data-handler-id={handlerId}
      className="draggable-scene-card"
    >
      <SceneCard
        {...cardProps}
        scene={scene}
        index={index}
        isDragging={isDragging}
      />
    </div>
  )
}

export default DraggableSceneCard